/**
*	xyz.h
*	The 2D && 3D coordination classes.
*	include two class xy and xyz, represent 2D and 3D vector.
*	And some function that related to coord operation.
*	By Mad @ 2002.2.19
*/

#include <cmath>
#include <cassert>
#include <kuai/tool/typedef.h>
#include <kuai/tool/constant.h>
#include <kuai/tool/phalanx.h>


#ifndef KUAIMISC_XYZ_H
#define KUAIMISC_XYZ_H

namespace kuai {

	/// a 2D vector. It acts like a complex.
	/**
	class XY is a template class which simulate the actions of a 2D vector.
	you may +, -, * &&/|| / them in directly math expression.
	The template type T is an math value type. May be the inner types such as
	int, long, double, float, || some other class such as boost::rational etc.
	*/
	template<typename T>
		class XY
	{
	// types.
	public:
		/// The type of members, int, double, float, for example.
		typedef T element_type;
		/// The type of it self
		typedef XY<T> self_type;
		/// The type of elemental pointers.
		typedef T* element_pointer;
		/// The type of const elemental pointers.
		typedef const T* const_element_pointer;

		/// The type of elemental pointers.
		typedef T& element_reference;
		/// The type of const elemental pointers.
		typedef const T& const_element_reference;


	// members:
	public:
		/// The 1st part (x) of the vector
		T x;
		/// The 2nd part (y) of the vector
		T y;


		/// default of the class, do nothing.
		XY() throw()
		{ }

		/// the class from two init value.
		XY(
			const T vx, const T vy
		) throw()
			: x(vx), y(vy)
		{ }

	// methods.

		/// add v0 to me
		inline XY&
			operator+=(
				const XY& v0
			) throw()
		{
			Me.x += v0.x;
			Me.y += v0.y;
			return Me;
		}

		/// sub v0 to me
		inline XY&
			operator-=(
				const XY& v0
			) throw()
		{
			Me.x -= v0.x;
			Me.y -= v0.y;
			return Me;
		}

		/// multi v0 to me (cross product)
		inline XY&
			operator*=(
				const XY& v0
			) throw()
		{
			T t = Me.x * v0.x - Me.y * v0.y;
			Me.y = Me.x * v0.y + Me.y * v0.x;
			Me.x = t;
			return Me;
		}

		/// multi by v0 (value product)
		inline XY&
			operator*=(
				T v0
			) throw()
		{
			Me.x *= v0;
			Me.y *= v0;
			return Me;
		}

		/// division by v0 (value division)
		inline XY&
			operator/=(
				T v0
			) throw()
		{
			Me.x /= v0;
			Me.y /= v0;
			return Me;
		}

	// methods:
		/// get the absolute value of the vector;
		inline RealNumber abs() const
		{
			return sqrt(x*x + y*y);
		}

		/// get the argument angle of the vector;
		inline RealNumber arg() const
		{
			return atan2(y, x);
		}
	};

	// +, -, *, /
	template <typename T>
	inline XY<T>
		operator+(
			const XY<T>& v1,
			const XY<T>& v2
		) throw()
	{
		XY<T> result(v1);
		result += v2;
		return result;
	}

	template <typename T>
	inline XY<T>
		operator-(
			const XY<T>& v1,
			const XY<T>& v2
		) throw()
	{
		XY<T> result(v1);
		result -= v2;
		return result;
	}

	template <typename T>
	inline XY<T>
		operator*(
			const XY<T>& v1,
			const XY<T>& v2
		) throw()
	{
		XY<T> result(v1);
		result *= v2;
		return result;
	}

	template <typename T>
	inline XY<T>
		operator*(					// Value product
			const XY<T>& v1,
			T v2
		) throw()
	{
		XY<T> result(v1);
		result *= v2;
		return result;
	}

	template <typename T>
	inline XY<T>
		operator*(						// Value prodct of xy
			T v1,
			const XY<T>& v2
		) throw()
	{
		XY<T> result(v2);
		result *= v1;
		return result;
	}

	template <typename T>
	inline XY<T>
		operator/(					// Value division
			const XY<T>& v1,
			T v2
		) throw()
	{
		XY<T> result(v1);
		result /= v2;
		return result;
	}

	// == && !=
	template <typename T>
	inline bool
		operator==(						// Compare
			const XY<T>& v1,
			const XY<T>& v2
		) throw()
	{
		return v1.x == v2.x && v1.y == v2.y;
	}

	template <typename T>
	inline bool
		operator!=(						// Compare
			const XY<T>& v1,
			const XY<T>& v2
		) throw()
	{
		return not (v1 == v2);
	}

	template<typename T>
	inline RealNumber
		abs(							// abs
			const XY<T>& v
		) throw()
	{
		return v.abs();
	}

	template<typename T>
	inline std::ostream&
		operator<<(
			std::ostream& os,
			const XY<T>& v0
		) throw()
	{
		return os << "(" << v0.x << ", " << v0.y << ")";
	}

	inline XY<RealNumber>
		polar(
			RealNumber abs, RealNumber theta
		)
	{
        return XY<RealNumber>(abs * cos(theta), abs * sin(theta));
	}

	inline XY<RealNumber>
		norm(RealNumber arg)
	{
		return XY<RealNumber>(cos(arg), sin(arg));
	}

	// class xy, a 2D vector.
	///////////////////////////////////////////////////////////////////




	///////////////////////////////////////////////////////////////////
	// class xyz, a 3D vector.
	template<typename T>
		class XYZ
	{
	// types.
	public:
		typedef T element_type;
		typedef T* element_pointer;
		typedef const T* const_element_pointer;

		/// The type of elemental pointers.
		typedef T& element_reference;
		/// The type of const elemental pointers.
		typedef const T& const_element_reference;

		typedef XYZ<T> self_type;

	// members.
	public:
		T x, y, z;

	// ctors && detor.
		// default ctor
		inline
			XYZ() throw()
		{ }

		inline
			XYZ(
				T vx,
				T vy,
				T vz
			) throw()
				: x(vx), y(vy), z(vz)
		{ }

	// operators.
		inline XYZ&
			operator+=(					// add
				const XYZ& v0
			) throw()
		{
			Me.x += v0.x;
			Me.y += v0.y;
			Me.z += v0.z;
			return Me;
		}

		inline XYZ&
			operator&=(					// add
				const XYZ& v0
			) throw()
		{
			Me.x *= v0.x;
			Me.y *= v0.y;
			Me.z *= v0.z;
			return Me;
		}

		inline XYZ&
			operator-=(					// sub
				const XYZ& v0
			) throw()
		{
			Me.x -= v0.x;
			Me.y -= v0.y;
			Me.z -= v0.z;
			return Me;
		}

		inline XYZ&
			operator*=(					// cross product
				const XYZ& v0
			) throw()
		{
			const XYZ t(Me);
			Me.x = t.y * v0.z - t.z * v0.y;
			Me.y = t.z * v0.x - t.x * v0.z;
			Me.z = t.x * v0.y - t.y * v0.x;
			return Me;
		}

		inline XYZ&
			operator*=(					// value product
				T v0
			) throw()
		{
			Me.x *= v0;
			Me.y *= v0;
			Me.z *= v0;
			return Me;
		}


		inline XYZ&
			operator/=(					// value division
				T v0
			) throw()
		{
			Me.x /= v0;
			Me.y /= v0;
			Me.z /= v0;
			return Me;
		}

		inline XYZ<T>
			operator-(void) const throw()// negative.
		{
			return XYZ(-Me.x, -Me.y, -Me.z);
		}
	};

	// +, -, *, /
	template <typename T>
	inline XYZ<T>
		operator+(
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		XYZ<T> result(v1);
		result += v2;
		return result;
	}

	template <typename T>
	inline XYZ<T>
		operator-(
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		XYZ<T> result(v1);
		result -= v2;
		return result;
	}

	template <typename T>
	inline XYZ<T>
		operator*(
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		XYZ<T> result(v1);
		result *= v2;
		return result;
	}

	template <typename T>
	inline XYZ<T>
		operator&(
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		XYZ<T> result(v1);
		result &= v2;
		return result;
	}

	template<typename T>
	inline XYZ<T>
		operator*(						// value product.
			T v1,
			const XYZ<T>& v2
		) throw()
	{
		XYZ<T> result(v2);
		result *= v1;
		return result;
	}

	template<typename T>
	inline XYZ<T>
		operator*(						// value product.
			const XYZ<T>& v1,
			T v2
		) throw()
	{
		XYZ<T> result(v1);
		result *= v2;
		return result;
	}

	template<typename T>
	inline XYZ<T>
		operator/(						// value division.
			const XYZ<T>& v1,
			T v2
		) throw()
	{
		XYZ<T> result(v1);
		result /= v2;
		return result;
	}

	// == && !=
	template <typename T>
	inline bool
		operator==(					// compare
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
	}

	template <typename T>
	inline bool
		operator!=(					// compare
			const XYZ<T>& v1,
			const XYZ<T>& v2
		) throw()
	{
		return ! (v1 == v2);
	}

	template<typename T>
	inline T
		dot(							// abs of xyz.
			const XYZ<T>& v1, const XYZ<T>& v2
		) throw()
	{
		return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
	}

	// square of abs(v)
	template<typename T>
	inline T
		abs2(							// abs of xyz.
			const XYZ<T>& v
		) throw()
	{
		return dot(v, v);
	}

	// abs
	template<typename T>
	inline RealNumber
		abs(							// abs of xyz.
			const XYZ<T>& v
		) throw()
	{
		return sqrt(abs2(v));
	}

	template<typename T>
	inline std::ostream&
		operator<<(
			std::ostream& os,
			const XYZ<T>& v0
		) throw()
	{
		return os << "(" << v0.x << ", " << v0.y << ", " << v0.z << ")";
	}


	// class xyz, an 3D vector.
	///////////////////////////////////////////////////////////////////



	/// degree-radian transform.
	inline RealNumber deg2rad(RealNumber v0)
	{
		return PI / 180 * v0;
	}

	/// radian-degree transform.
	inline RealNumber rad2deg(RealNumber v0)
	{
		return 180 / PI * v0;
	}


	///////////////////////////////////////////////////////////////////
	// typedef XYZ<double> Coord3D

	typedef XYZ<RealNumber> 				Coord3D;
	typedef Phalanx<RealNumber, 4> 			TranslateMatrix;

	// return the angle between two vev1 && v2;
	RealNumber
		angle(
			const Coord3D& v1, const Coord3D& v2
		);
	inline RealNumber
		angle(
			const Coord3D& v1,
			const Coord3D& v2,
			const Coord3D& v3
		)
	{
		return angle(v2-v1, v2-v3);
	}

	Coord3D
		operator*(
			const Coord3D& v1, const TranslateMatrix& v2
		);

	Coord3D
		operator*(
			const TranslateMatrix& v2, const Coord3D& v1
		);

	TranslateMatrix
		TransMatrix(
			const Coord3D& v0
		);

	TranslateMatrix
		RotateMatrix(
			const Coord3D& vAxis, RealNumber angle
		);

	TranslateMatrix
		RotateMatrix(
			const Coord3D& vAxis1, const Coord3D& vAxis2, RealNumber angle
		);


	inline Coord3D
		rotate(
			const Coord3D& v0,  const Coord3D& vAxis, RealNumber angle
		)
	{
		return v0 * RotateMatrix(vAxis, angle);
	}

	inline Coord3D
		rotate(
			const Coord3D& v0,
			const Coord3D& vAxis1, const Coord3D& vAxis2,
			RealNumber angle
		)
	{
		return rotate(v0-vAxis1, vAxis2 - vAxis1, angle)+vAxis1;
	}

	// typedef xyz<double> Coord3D
	///////////////////////////////////////////////////////////////////




}

#endif // KUAIMISC_XYZ_H

